package se.webbzon.oschi01.world;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import se.webbzon.oschi01.region.RegionReader;
import se.webbzon.oschi01.region.RegionReference;
import se.webbzon.oschi01.singleton.Singletons;
import se.webbzon.oschi01.utils.FileUtils;
import se.webbzon.oschi01.utils.Reader;
import se.webbzon.oschi01.utils.Writer;

public class FileWorld implements World {
	
	private static String[] regionSuffixes = {".mca",".mcr"};
	
	private final File dir;
	
	private final List<Dimension> dimensions;
	
	public FileWorld(File dir) throws IllegalArgumentException, IOException {
		if (!dir.isDirectory())
			throw new IllegalArgumentException("The root file of the world must be a directory.");
		this.dir = dir;
		dimensions = new ArrayList<Dimension>();
		getDimensions(dir,dir,dimensions);
	}
	
	@Override public boolean addDimension(Dimension dimension) {
		return dimensions.add(dimension);
	}

	@Override public boolean removeDimension(Dimension dimension) {
		return dimensions.remove(dimension);
	}
	
	@Override public boolean hasDimension(Dimension dimension) {
		return dimensions.contains(dimension);
	}

	@Override public int dimensions() {
		return dimensions.size();
	}

	@Override public Dimension getDimension(int index) {
		return dimensions.get(index);
	}
	
	@Override public Dimension getDimension(String name) {
		for (Dimension dimension : dimensions) {
			if (dimension.getName().equals(name))
				return dimension;
		}
		return null;
	}
	
	@Override public boolean hasDatum(String resource) {
		File file = new File(dir,resource);
		return file.exists();
	}

	@Override public Collection<String> getData() {
		Collection<String> resources = new ArrayList<String>();
		getResources("",dir,resources);
		return resources;
	}
	
	@Override public <T> T getDatum(String resource, Reader<T, InputStream> reader) throws IOException {
		File file = new File(dir,resource);
		try (FileInputStream fis = new FileInputStream(file)) {
			T datum = reader.read(fis);
			return datum;
		} catch (IOException e) {
			throw e;
		} catch (Throwable e) {
			throw new IOException(e);
		}
	}

	@Override public <T> void setDatum(String resource, T datum, Writer<T, OutputStream> writer) throws IOException {
		File file = new File(dir,resource);
		if (file.exists())
			file.delete();
		file.createNewFile();
		try (FileOutputStream fos = new FileOutputStream(file)) {
			writer.write(datum, fos);
		} catch (IOException e) {
			throw e;
		} catch (Throwable e) {
			throw new IOException(e);
		}
	}

	
	private static final void getResources(String parent, File file, Collection<String> resources) {
		for (File child : file.listFiles()) {
			if (child.isDirectory()) {
				String prefix = parent + FileUtils.getFileName(child) + "\\";
				getResources(prefix,child,resources);
			} else if (child.isFile()) {
				String fileName = FileUtils.getFileName(child);
				boolean exclude = false;
				for (String suffix : regionSuffixes) {
					if (fileName.endsWith(suffix)) {
						exclude = true;
						break;
					}
				}
				if (!exclude) {
					resources.add(parent + fileName);
				}
			}
		}
	}
	
	private static final void getDimensions(File root, File file, List<Dimension> dimensions) throws IOException {
		Dimension dim = null;
		RegionReader reader = null;
		for (File child : file.listFiles()) {
			if (child.isDirectory()) {
				getDimensions(root,child,dimensions);
			} else {
				String fileName = FileUtils.getFileName(child);
				for (String suffix : regionSuffixes) {
					if (fileName.endsWith(suffix)) {
						if (dim == null) {
							String dimensionName;
							if (file.getParent() == null || file.getParentFile().equals(root))
								dimensionName = "";
							else
								dimensionName = FileUtils.getFileName(file.getParentFile());
							dim = new DefaultDimension(dimensionName);
							dimensions.add(dim);
							reader = Singletons.instance(RegionReader.class);
						}
						String[] parts = fileName.split("\\.");
						int x = Integer.parseInt(parts[1]);
						int z = Integer.parseInt(parts[2]);
						RegionReference ref = new RegionReference(child.toURI().toURL());
						Region region = reader.read(ref);
						region.setCoordinate(x, z);
						dim.addRegion(region);
					}
				}
			}
		}
	}

}
